package dao

import (
	"context"
	"database/sql"
	"errors"
	"github.com/go-sql-driver/mysql"
	"gorm.io/gorm"
	"time"
)

var (
	ErrDuplicateEmail = errors.New("邮箱冲突")     //预定义错误 希望别人知道这里发生的事情
	ErrRecordNotFound = gorm.ErrRecordNotFound //表示未找到
	ErrDataTooLong    = gorm.ErrInvalidData    //errors.New("用户名或个人简介超出字数限制")
)

type User struct {
	Id          int64          `gorm:"primaryKey,autoIncrement"` //自增主键
	Email       sql.NullString `gorm:"unique"`                   //唯一索引
	Password    string
	UserName    string `gorm:"size:64"`
	Birthday    int64
	UserProfile string `gorm:"size:2048"`
	Ctime       int64  //不用time.Time 时区问题 统一用UTC 0的毫秒数
	Utime       int64
	Phone       sql.NullString `gorm:"unique"`
	//1.如果查询要求同时使用openid和unionid 创建联合唯一索引
	//2.如果查询只用openid 就openid上创建唯一索引 或是<openid,unionid>创建联合索引 注意顺序
	//3.如果查询只用unionid 就unionid上创建唯一索引 或是<unionid,openid>创建联合索引 注意顺序
	WechatOpenId  sql.NullString `gorm:"unique"`
	WechatUnionId sql.NullString
	//sql.NullString 代表可以为NULL的列
	//Email *string
}

type UserDao interface {
	Insert(ctx context.Context, u User) error
	FindByEmail(ctx context.Context, email string) (User, error)
	Update(ctx context.Context, u User) error
	FindById(ctx context.Context, userId int64) (User, error)
	FindByPhone(ctx context.Context, phone string) (User, error)
	FindByWechat(ctx context.Context, openId string) (User, error)
}

type GORMUserDAO struct {
	db *gorm.DB
}

func NewUserDAO(db *gorm.DB) UserDao {
	return &GORMUserDAO{db: db}
}

// Insert 接近数据库的语义
func (dao *GORMUserDAO) Insert(ctx context.Context, u User) error {
	now := time.Now().UnixMilli()
	u.Ctime = now
	u.Utime = now
	err := dao.db.WithContext(ctx).Create(&u).Error //important gorm 永远用指针！！
	var mysqlErr *mysql.MySQLError
	if errors.As(err, &mysqlErr) {
		const duplicateErrCode uint16 = 1062
		if mysqlErr.Number == duplicateErrCode { //邮箱冲突 用户冲突
			return ErrDuplicateEmail
		}
	}
	//if mysqlErr,ok := err.(*mysql.MySQLError); ok {
	//
	//}
	return err
}

func (dao *GORMUserDAO) FindByEmail(ctx context.Context, email string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("email=?", email).First(&u).Error //Find也可以
	return u, err
}
func (dao *GORMUserDAO) Update(ctx context.Context, u User) error {
	updateErr := dao.db.WithContext(ctx).Where("id = ?", u.Id).Updates(&u).Error
	var mysqlErr *mysql.MySQLError
	if errors.As(updateErr, &mysqlErr) {
		const DataTooLongErrCode uint16 = 1406
		if mysqlErr.Number == DataTooLongErrCode { //邮箱冲突 用户冲突
			return ErrDataTooLong
		}
	}
	return updateErr
}
func (dao *GORMUserDAO) FindById(ctx context.Context, userId int64) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("id=?", userId).First(&u).Error //Find也可以
	return u, err
}

func (dao *GORMUserDAO) FindByPhone(ctx context.Context, phone string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("phone=?", phone).First(&u).Error //Find也可以
	return u, err
}
func (dao *GORMUserDAO) FindByWechat(ctx context.Context, openId string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("wechat_open_id=?", openId).First(&u).Error //Find也可以
	return u, err
}
